package repositories

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"gin-elas-student-info/config"
	"gin-elas-student-info/model"
	"github.com/elastic/go-elasticsearch/v8/esapi"
	"strings"
)

var (
	IndexName = "student"
)

type StudentRepository struct {
}

func (r *StudentRepository) Create(student *model.Student) error {
	data, err := json.Marshal(student)
	if err != nil {
		return err
	}
	req := esapi.IndexRequest{
		Index:      IndexName,
		DocumentID: student.ID,
		Body:       strings.NewReader(string(data)),
		Refresh:    "true",
	}

	_, err = req.Do(context.Background(), config.Es)
	return err
}

func (r *StudentRepository) GetByID(id string) (*model.Student, error) {
	req := esapi.GetRequest{
		Index:      IndexName,
		DocumentID: id,
	}

	res, err := req.Do(context.Background(), config.Es)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.IsError() {
		return nil, fmt.Errorf(res.String())
	}
	var result struct {
		Source model.Student `json:"_source"`
	}
	if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
		return nil, err
	}
	return &result.Source, nil
}

func (r *StudentRepository) Update(id string, s *model.Student) error {
	data, err := json.Marshal(s)
	if err != nil {
		return err
	}

	req := esapi.UpdateRequest{
		Index:      IndexName,
		DocumentID: id,
		Body:       strings.NewReader(string(data)),
		Refresh:    "true",
	}

	_, err = req.Do(context.Background(), config.Es)
	return err
}
func (r *StudentRepository) Delete(id string) error {
	req := esapi.DeleteRequest{
		Index:      IndexName,
		DocumentID: id,
		Refresh:    "true",
	}
	_, err := req.Do(context.Background(), config.Es)
	return err
}
func (r *StudentRepository) Search(query string) ([]model.Student, error) {
	var buf bytes.Buffer
	searchQuery := map[string]interface{}{
		"query": map[string]interface{}{
			"multi_match": map[string]interface{}{
				"query":  query,
				"fields": []string{"name", "major", "grade", "class_name"},
			},
		},
	}
	if err := json.NewEncoder(&buf).Encode(searchQuery); err != nil {
		return nil, err
	}
	res, err := config.Es.Search(
		config.Es.Search.WithContext(context.Background()),
		config.Es.Search.WithIndex(IndexName),
		config.Es.Search.WithBody(&buf),
		config.Es.Search.WithTrackTotalHits(true),
	)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	var result struct {
		Hits struct {
			Hits []struct {
				Source model.Student `json:"_source"`
			} `json:"hits"`
		} `json:"hits"`
	}

	if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
		return nil, err
	}
	students := make([]model.Student, len(result.Hits.Hits))
	for i, hit := range result.Hits.Hits {
		students[i] = hit.Source
	}
	return students, nil
}

func (c *StudentRepository) GetAllStudents(page, size int) ([]model.Student, int64, error) {
	var buf bytes.Buffer
	query := map[string]interface{}{
		"query": map[string]interface{}{
			"match_all": map[string]interface{}{},
		},
		"from": (page - 1) * size,
		"size": size,
	}
	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		return nil, 0, err
	}
	res, err := config.Es.Search(
		config.Es.Search.WithContext(context.Background()),
		config.Es.Search.WithIndex(IndexName),
		config.Es.Search.WithBody(&buf),
		config.Es.Search.WithTrackTotalHits(true),
	)
	if err != nil {
		return nil, 0, err
	}
	defer res.Body.Close()
	var result struct {
		Hits struct {
			Total struct {
				Value int64 `json:"value"`
			} `json:"total"`
			Hits []struct {
				Source model.Student `json:"_source"`
			} `json:"hits"`
		} `json:"hits"`
	}
	if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
		return nil, 0, err
	}
	students := make([]model.Student, len(result.Hits.Hits))
	for i, hit := range result.Hits.Hits {
		students[i] = hit.Source
	}
	return students, result.Hits.Total.Value, nil
}

// 在 StudentRepository 结构体中添加新方法
func (r *StudentRepository) SearchByConditions(conditions map[string]interface{}) ([]model.Student, error) {
	var buf bytes.Buffer
	query := map[string]interface{}{
		"query": map[string]interface{}{
			"bool": map[string]interface{}{
				"must": []map[string]interface{}{},
			},
		},
	}

	// 构建查询条件
	for field, value := range conditions {
		if value != "" && value != nil {
			switch field {
			case "name", "major", "address":
				// 文本字段使用模糊匹配
				query["query"].(map[string]interface{})["bool"].(map[string]interface{})["must"] = append(
					query["query"].(map[string]interface{})["bool"].(map[string]interface{})["must"].([]map[string]interface{}),
					map[string]interface{}{
						"match": map[string]interface{}{
							field: value,
						},
					},
				)
			case "grade", "class_name":
				// 关键字字段使用精确匹配
				query["query"].(map[string]interface{})["bool"].(map[string]interface{})["must"] = append(
					query["query"].(map[string]interface{})["bool"].(map[string]interface{})["must"].([]map[string]interface{}),
					map[string]interface{}{
						"term": map[string]interface{}{
							field: value,
						},
					},
				)
			case "min_age":
				// 年龄范围查询
				query["query"].(map[string]interface{})["bool"].(map[string]interface{})["must"] = append(
					query["query"].(map[string]interface{})["bool"].(map[string]interface{})["must"].([]map[string]interface{}),
					map[string]interface{}{
						"range": map[string]interface{}{
							"age": map[string]interface{}{
								"gte": value,
							},
						},
					},
				)
			case "max_age":
				query["query"].(map[string]interface{})["bool"].(map[string]interface{})["must"] = append(
					query["query"].(map[string]interface{})["bool"].(map[string]interface{})["must"].([]map[string]interface{}),
					map[string]interface{}{
						"range": map[string]interface{}{
							"age": map[string]interface{}{
								"lte": value,
							},
						},
					},
				)
			}
		}
	}

	// 如果没有查询条件，则返回所有文档
	if len(query["query"].(map[string]interface{})["bool"].(map[string]interface{})["must"].([]map[string]interface{})) == 0 {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"match_all": map[string]interface{}{},
			},
		}
	}

	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		return nil, err
	}

	res, err := config.Es.Search(
		config.Es.Search.WithContext(context.Background()),
		config.Es.Search.WithIndex(IndexName),
		config.Es.Search.WithBody(&buf),
		config.Es.Search.WithTrackTotalHits(true),
		config.Es.Search.WithPretty(),
	)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	var result struct {
		Hits struct {
			Hits []struct {
				Source model.Student `json:"_source"`
			} `json:"hits"`
		} `json:"hits"`
	}

	if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
		return nil, err
	}

	students := make([]model.Student, len(result.Hits.Hits))
	for i, hit := range result.Hits.Hits {
		students[i] = hit.Source
	}

	return students, nil
}
